﻿#region Includes
using System;
using System.IO;
using System.Drawing;
using System.Collections.Generic;

using OpenTK;
using OpenTK.Graphics.OpenGL;

using GingerGL;
using GingerGL.Math;
#endregion

namespace GingerGL.Graphics
{
    //------------------------------------------------------------//
    /// <summary>
    /// Skybox Class
    /// </summary>
    public class Skybox : IRenderable
    {
        //--------------------------------------------------------//
        #region Variables
        private Texture[] Textures;
        #endregion
        //--------------------------------------------------------//
        #region Properties
        /// <summary>
        /// Gets/Sets TRUE is object is visible
        /// </summary>
        public bool Visible { get; set; }
        #endregion
        //--------------------------------------------------------//
        #region Constructor
        /// <summary>
        /// Initialize new instance of object
        /// </summary>
        public Skybox() { this.Textures = new Texture[6]; this.Visible = true; }
        #endregion
        //--------------------------------------------------------//
        #region Methods
        /// <summary>
        /// Draw Skybox
        /// </summary>
        public void Draw()
        {
            Vector3 position = Engine.Instance.CameraPosition;

            GL.PushAttrib(AttribMask.EnableBit | AttribMask.DepthBufferBit | AttribMask.PolygonBit | AttribMask.TextureBit);
            GL.MatrixMode(MatrixMode.Texture);
            GL.LoadIdentity();
            GL.MatrixMode(MatrixMode.Modelview);
            GL.PushMatrix();
            GL.Disable(EnableCap.TextureGenS);
            GL.Disable(EnableCap.TextureGenT);
            GL.PolygonMode(MaterialFace.Front, PolygonMode.Fill);
            GL.Disable(EnableCap.Blend);
            GL.Disable(EnableCap.AlphaTest);
            GL.Disable(EnableCap.DepthTest);
            GL.CullFace(CullFaceMode.Back);
            GL.DepthMask(false);            
            GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Replace);
            this.Textures[0].Apply();
            // Front
            GL.Begin(BeginMode.Quads);
            GL.TexCoord2(0f, 0f);
            GL.Vertex3(position.X - 10f, position.Y - 10f, position.Z - 10f);
            GL.TexCoord2(1f, 0f);
            GL.Vertex3(position.X + 10f, position.Y - 10f, position.Z - 10f);
            GL.TexCoord2(1f, 1f);
            GL.Vertex3(position.X + 10f, position.Y + 10f, position.Z - 10f);
            GL.TexCoord2(0f, 1f);
            GL.Vertex3(position.X - 10f, position.Y + 10f, position.Z - 10f);
            GL.End();
            // Right
            this.Textures[1].Apply();
            GL.Begin(BeginMode.Quads);
            GL.TexCoord2(0f, 0f);
            GL.Vertex3(position.X + 10f, position.Y - 10f, position.Z - 10f);
            GL.TexCoord2(1f, 0f);
            GL.Vertex3(position.X + 10f, position.Y - 10f, position.Z + 10f);
            GL.TexCoord2(1f, 1f);
            GL.Vertex3(position.X + 10f, position.Y + 10f, position.Z + 10f);
            GL.TexCoord2(0f, 1f);
            GL.Vertex3(position.X + 10f, position.Y + 10f, position.Z - 10f);
            GL.End();
            // Back
            this.Textures[2].Apply();
            GL.Begin(BeginMode.Quads);
            GL.TexCoord2(0f, 0f);
            GL.Vertex3(position.X + 10f, position.Y - 10f, position.Z + 10f);
            GL.TexCoord2(1f, 0f);
            GL.Vertex3(position.X - 10f, position.Y - 10f, position.Z + 10f);
            GL.TexCoord2(1f, 1f);
            GL.Vertex3(position.X - 10f, position.Y + 10f, position.Z + 10f);
            GL.TexCoord2(0f, 1f);
            GL.Vertex3(position.X + 10f, position.Y + 10f, position.Z + 10f);
            GL.End();
            // Left
            this.Textures[3].Apply();
            GL.Begin(BeginMode.Quads);
            GL.TexCoord2(0f, 0f);
            GL.Vertex3(position.X - 10f, position.Y - 10f, position.Z + 10f);
            GL.TexCoord2(1f, 0f);
            GL.Vertex3(position.X - 10f, position.Y - 10f, position.Z - 10f);
            GL.TexCoord2(1f, 1f);
            GL.Vertex3(position.X - 10f, position.Y + 10f, position.Z - 10f);
            GL.TexCoord2(0f, 1f);
            GL.Vertex3(position.X - 10f, position.Y + 10f, position.Z + 10f);
            GL.End();
            // Top
            this.Textures[4].Apply();
            GL.Begin(BeginMode.Quads);
            GL.TexCoord2(0f, 0f);
            GL.Vertex3(position.X - 10f, position.Y + 10f, position.Z - 10f);
            GL.TexCoord2(1f, 0f);
            GL.Vertex3(position.X + 10f, position.Y + 10f, position.Z - 10f);
            GL.TexCoord2(1f, 1f);
            GL.Vertex3(position.X + 10f, position.Y + 10f, position.Z + 10f);
            GL.TexCoord2(0f, 1f);
            GL.Vertex3(position.X - 10f, position.Y + 10f, position.Z + 10f);
            GL.End();
            // Bottom
            this.Textures[5].Apply();
            GL.Begin(BeginMode.Quads);
            GL.TexCoord2(0f, 0f);
            GL.Vertex3(position.X - 10f, position.Y - 10f, position.Z + 10f);
            GL.TexCoord2(1f, 0f);
            GL.Vertex3(position.X + 10f, position.Y - 10f, position.Z + 10f);
            GL.TexCoord2(1f, 1f);
            GL.Vertex3(position.X + 10f, position.Y - 10f, position.Z - 10f);
            GL.TexCoord2(0f, 1f);
            GL.Vertex3(position.X - 10f, position.Y - 10f, position.Z - 10f);
            GL.End();

            GL.Enable(EnableCap.DepthTest);
            GL.DepthMask(true);
            GL.CullFace(CullFaceMode.Front);
            GL.PopMatrix();
            GL.PopAttrib();
        }
        /// <summary>
        /// Loads Standard Skybox
        /// </summary>
        static public Skybox Load(params string[] textures)
        {
            if (textures.Length != 6) throw new ArgumentException("6 Textures needed");

            Skybox sbox = new Skybox();
            for (int ndex = 0; ndex < textures.Length; ndex++)
                sbox.Textures[ndex] = Texture.Instance.GetTexture(textures[ndex],true);

            return sbox;
        }
        /// <summary>
        /// Loads Standard Skybox
        /// </summary>
        static public Skybox Load(params Image[] textures)
        {
            if (textures.Length != 6) throw new ArgumentException("6 Textures needed");

            Skybox sbox = new Skybox();
            for (int ndex = 0; ndex < textures.Length; ndex++)
                sbox.Textures[ndex] = Texture.Instance.FromImage(textures[ndex], true);

            return sbox;
        }
        /// <summary>
        /// Disposes of objects
        /// </summary>
        public void Dispose()
        {
            foreach (Texture t in this.Textures)
                t.Dispose();
            this.Textures = null;
        }
        #endregion
        //--------------------------------------------------------//
    }
    //------------------------------------------------------------//
}
